﻿// <copyright file="CpFileHeader.cs" company="Sinclair Community College">
// Copyright 2010 Sinclair Community College
// </copyright>
// <originalAuthor>Patrick S. Seymour (patrick.seymour@gmail.com)</originalAuthor>

namespace SinclairCC.AppV
{
    using System;
    using System.Diagnostics.CodeAnalysis;

    /// <summary>
    /// This class represents the header that is found at the beginning of all App-V CP files.
    /// </summary>
    public class CpFileHeader
    {
        /// <summary>
        /// This field has the value 0x00010000.
        /// </summary>
        private int identifier;

        /// <summary>
        /// The absolute byte offset pointer, from the beginning of the CP file to the start
        /// of the first section.
        /// </summary>
        /// <remarks>
        /// This value is the initial link through which all of the sections can be found.
        /// </remarks>
        private uint firstSectionOffset;

        /// <summary>
        /// This field is reserved and is always initialized to 0xFFFFFFFE.
        /// </summary>
        [SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields", Justification = "This field is marked as reserved in the App-V File Format Specification.")]
        private byte[] reserved1;

        /// <summary>
        /// This field has the value 0x00000038 (decimal 56).
        /// </summary>
        /// <remarks>
        /// This is the size of the CP file header section.
        /// </remarks>
        private int headerSize;

        /// <summary>
        /// The unique identifier (GUID) for the CP file.
        /// </summary>
        /// <remarks>
        /// This identifier is not related to the package or version GUIDs found in the associated
        /// SFT file, but rather it is a unique GUID generated just for the CP file at the time of creation.
        /// </remarks>
        private Guid uniqueId;

        /// <summary>
        /// The total number of sections to be found in the CP file, including the header section.
        /// </summary>
        private int sectionCount;

        /// <summary>
        /// This field is reserved and is initialized to all zeroes.
        /// </summary>
        [SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields", Justification = "This field is marked as reserved in the App-V File Format Specification.")]
        private byte[] reserved2;

        /// <summary>
        /// The minimum build number for a client required to run the virtual environment configuration.
        /// </summary>
        /// <remarks>This value should be zero (0).</remarks>
        private short requiredClientVersionBuild;

        /// <summary>
        /// The minimum revision number for a client required to run the virtual environment configuration.
        /// </summary>
        /// <remarks>This value should be zero (0).</remarks>
        private short requiredClientVersionRevision;

        /// <summary>
        /// The minimum minor version number for a client required to run the virtual environment configuration.
        /// </summary>
        /// <remarks>This value should be one (1).</remarks>
        private short requiredClientVersionMinor;

        /// <summary>
        /// The minimum major version number for a client required to run the virtual environment configuration.
        /// </summary>
        /// <remarks>This value should be three (3).</remarks>
        private short requiredClientVersionMajor;

        /// <summary>
        /// The build number for the App-V Sequencer version used to create the CP file initially.
        /// </summary>
        /// <remarks>The default value should be zero (0).</remarks>
        private short sequencerVersionBuild;

        /// <summary>
        /// The revision number for the App-V Sequencer version used to create the CP file initially.
        /// </summary>
        /// <remarks>The default value should be zero (0).</remarks>
        private short sequencerVersionRevision;

        /// <summary>
        /// The minor version number for the App-V Sequencer version used to create the CP file initially.
        /// </summary>
        /// <remarks>The default value should be zero (0).</remarks>
        private short sequencerVersionMinor;

        /// <summary>
        /// The major version number for the App-V Sequencer version used to create the CP file initially.
        /// </summary>
        /// <remarks>The default value should be zero (0).</remarks>
        private short sequencerVersionMajor;

        /// <summary>
        /// A binary reader that points to the CP file.
        /// </summary>
        private System.IO.BinaryReader fileReader;

        /// <summary>
        /// Initializes a new instance of the CpFileHeader class.
        /// </summary>
        /// <param name="fileReader">
        /// A binary reader that points to the CP file.
        /// </param>
        public CpFileHeader(System.IO.BinaryReader fileReader)
        {
            this.fileReader = fileReader;
            this.ReadSection();
        }

        /// <summary>
        /// Gets the absolute byte offset pointer, from the beginning of the CP file to the start
        /// of the first section.
        /// </summary>
        /// <remarks>
        /// This value is the initial link through which all of the sections can be found.
        /// </remarks>
        public uint FirstSectionOffset
        {
            get { return this.firstSectionOffset; }
        }

        /// <summary>
        /// Gets the unique identifier (GUID) for the CP file.
        /// </summary>
        /// <remarks>
        /// This identifier is not related to the package or version GUIDs found in the associated
        /// SFT file, but rather it is a unique GUID generated just for the CP file at the time of creation.
        /// </remarks>
        public Guid UniqueId
        {
            get { return this.uniqueId; }
        }

        /// <summary>
        /// Gets the total number of sections to be found in the CP file, including the header section.
        /// </summary>
        public int SectionCount
        {
            get { return this.sectionCount; }
        }

        /// <summary>
        /// Gets the minimum client version required to run the virtual environment configuration.
        /// </summary>
        /// <remarks>The default value should be 3.1.0.0.</remarks>
        public Version RequiredClientVersion
        {
            get { return new Version(this.requiredClientVersionMajor, this.requiredClientVersionMinor, this.requiredClientVersionBuild, this.requiredClientVersionRevision); }
        }

        /// <summary>
        /// Gets the version number for the App-V Sequencer used to create the CP file initially.
        /// </summary>
        /// <remarks>The default value should be 0.0.0.0.</remarks>
        public Version SequencerVersion
        {
            get { return new Version(this.sequencerVersionMajor, this.sequencerVersionMinor, this.sequencerVersionBuild, this.sequencerVersionRevision); }
        }

        /// <summary>
        /// Reads the actual data for this section from the file.
        /// </summary>
        private void ReadSection()
        {
            this.identifier = this.fileReader.ReadInt32();
            this.firstSectionOffset = this.fileReader.ReadUInt32();
            this.reserved1 = this.fileReader.ReadBytes(4);
            this.headerSize = this.fileReader.ReadInt32();

            byte[] guidBytes = this.fileReader.ReadBytes(16);
            this.uniqueId = new Guid(guidBytes);

            this.sectionCount = this.fileReader.ReadInt32();
            this.reserved2 = this.fileReader.ReadBytes(4);

            this.requiredClientVersionBuild = this.fileReader.ReadInt16();
            this.requiredClientVersionRevision = this.fileReader.ReadInt16();
            this.requiredClientVersionMinor = this.fileReader.ReadInt16();
            this.requiredClientVersionMajor = this.fileReader.ReadInt16();

            this.sequencerVersionBuild = this.fileReader.ReadInt16();
            this.sequencerVersionRevision = this.fileReader.ReadInt16();
            this.sequencerVersionMinor = this.fileReader.ReadInt16();
            this.sequencerVersionMajor = this.fileReader.ReadInt16();
        }
    }
}
